#include "accurateAddressMappingConfig.h"

AddressMappingConfig::AddressMappingConfig()
{


    // Initialize time parameters
    this->tAL = 0;
    this->tCL = 0;
    this->tCWL = 0; // Column write latency, if applicable
    this->tRCD = 0; // Row activation time
    this->tRP = 0; // Row precharge time
    this->tBurstCycle = 0;

    this->rowPolicy = MyRowPolicy::CLOSED;
    this->queuePolicy = MyQueuePolicy::PER_BANK;
    this->trans_queue_size = 0;
    this->cmd_queue_size = 0; // Command queue size, if applicable

    this->memSize = 0;
    this->device_width = 0;

    this->ranks = 0;
    this->bankGroups = 0;
    this->banksPerGroup = 0;
    this->rows = 0;
    this->columns = 0;

    this->rankOffset = 0;
    this->bankGroupOffset = 0;
    this->bankOffset = 0;
    this->rowOffset = 0;
    this->columnOffset = 0;

    this->rankMask = 0;
    this->bankGroupMask = 0;
    this->bankMask = 0;
    this->rowMask = 0;
    this->columnMask = 0;
}

AddressMappingConfig::AddressMappingConfig(TimeParameters *TP, AddressMappingParameters *AMP, PolicyParameters *PP)
{

    // Initialize time parameters
    this->tAL = TP->tAL;
    this->tCL = TP->tCL;
    this->tCWL = TP->tCWL; // Column write latency, if applicable
    this->tRCD = TP->tRCD; // Row activation time
    this->tRP = TP->tRP; // Row precharge time
    this->tRAS = TP->tRAS; // Row active time, if applicable
    this->tBurstCycle = TP->tBurstCycle;
    this->tRRD = TP->tRRD; // Minimum time interval between two ACTIVATE commands to different banks within the same rank
    this->tRRD_L = TP->tRRD_L; // Minimum time interval
    this->tRRD_S = TP->tRRD_S; // Minimum time interval between two ACTIVATE commands to banks in different bank groups in DDR4
    this->tFAW = TP->tFAW; // Fast access window, if

    // Initialize policy parameters
    this->rowPolicy = PP->rowPolicy;
    this->queuePolicy = PP->queuePolicy;
    this->trans_queue_size = PP->transQueueSize;
    this->cmd_queue_size = PP->cmdQueueSize; // Command queue size, if applicable
    // Initialize DRAM parameters

    this->memSize = AMP->memSize;
    this->bus_width = AMP->bus_width; // Width of the bus in bits
    this->device_width = AMP->device_width;
    this->bankGroups = AMP->bankGroups;
    this->banksPerGroup = AMP->banksPerGroup;
    this->rows = AMP->rows;
    this->columns = AMP->columns;
    this->BL = AMP->BL; // Burst length in bytes (B), must be a power of 2

    SST::UnitAlgebra device_width_unit = SST::UnitAlgebra("1B") * this->device_width / 8;
    SST::UnitAlgebra page_size = device_width_unit * this->columns;
    SST::UnitAlgebra bank_size = page_size * this->rows;
    SST::UnitAlgebra rank_size = bank_size * this->banksPerGroup * this->bankGroups * (this->bus_width / this->device_width);
    SST::UnitAlgebra channel_size = SST::UnitAlgebra("1B") * this->memSize;
    SST::UnitAlgebra rank_number_with_unit = channel_size / rank_size;
    this->ranks = rank_number_with_unit.getRoundedValue();


    uint64_t request_size_bytes = bus_width / 8 * BL;
    shift_bits = ALogBase2(request_size_bytes);
    int col_low_bits = ALogBase2(BL);
    int actual_col_bits = ALogBase2(columns) - col_low_bits;
    std::cout << "shift_bits: " << shift_bits << " actual_col_bits: " << actual_col_bits << std::endl;



    this->rankMask = (1ULL << ALogBase2(this->ranks)) - 1;
    this->bankGroupMask = (1ULL << ALogBase2(this->bankGroups)) - 1;
    this->bankMask = (1ULL << ALogBase2(this->banksPerGroup)) - 1;
    this->rowMask = (1ULL << ALogBase2(this->rows)) - 1;
    this->columnMask = (1ULL << actual_col_bits) - 1;


    this->columnOffset = 0;
    this->rowOffset = actual_col_bits + this->columnOffset;
    this->bankOffset = rowOffset + ALogBase2(this->rows);
    this->bankGroupOffset = bankOffset + ALogBase2(this->banksPerGroup);
    this->rankOffset = bankGroupOffset + ALogBase2(this->bankGroups);



    std::cout << "columnMask: " << this->columnMask << " rowMask: " << this->rowMask
              << " bankMask: " << this->bankMask << " bankGroupMask: " << this->bankGroupMask
              << " rankMask: " << this->rankMask << std::endl;
    
    std::cout << "columnOffset: " << this->columnOffset << " rowOffset: " << this->rowOffset
                << " bankOffset: " << this->bankOffset << " bankGroupOffset: "
                << this->bankGroupOffset << " rankOffset: " << this->rankOffset << std::endl;

}

uint64_t AddressMappingConfig::ALogBase2(uint64_t power_of_two) {
    uint64_t i = 0;
    while (power_of_two > 1) {
        power_of_two /= 2;
        i++;
    }
    return i;
}
